﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
//using DatabaseHandler;
using Emgu.CV;
using Emgu.CV.Features2D;
using Emgu.CV.Flann;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using tessnet2;

namespace ImageHandler
{
	public static class ImageHandler
	{

		public static int FlannFindPairs(ImageFeature[] modelDescriptors, ImageFeature[] imageDescriptors, ref List<KeyValuePair<int, int>> ptPairs)
		{
			//Check if we have some valid model descriptors
			if (modelDescriptors.Length == 0)
				return -1;

			int length = modelDescriptors[0].Descriptor.Length;

			//Create matrix object and matrix image
			var matrixModel = new Matrix<float>(modelDescriptors.Length, length);
			var matrixImage = new Matrix<float>(imageDescriptors.Length, length);


			//copy model descriptors into matrixModel
			int row = 0;
			foreach (var modelDescriptor in modelDescriptors)
			{
				for (int i = 0; i < modelDescriptor.Descriptor.Length; i++)
				{
					matrixModel[row, i] = modelDescriptor.Descriptor[i];
				}

				row++;
			}

			//copy image descriptors into matrixImage
			row = 0;
			foreach (var imageDescriptor in imageDescriptors)
			{
				for (int i = 0; i < imageDescriptor.Descriptor.Length; i++)
				{
					matrixImage[row, i] = imageDescriptor.Descriptor[i];
				}

				row++;
			}

			//create return matrices for KnnSearch
			var indices = new Matrix<int>(modelDescriptors.Length, 2);
			var dists = new Matrix<float>(modelDescriptors.Length, 2);

			//create our flannIndex
			var flannIndex = new Index(matrixImage);

			//do the search
			flannIndex.KnnSearch(matrixModel, indices, dists, 2, 2);

			//filter out all unnecessary pairs based on distance between pairs
			int pairCount = 0;
			for (int i = 0; i < indices.Rows; i++)
			{
				if (dists.Data[i, 0] < 0.6 * dists.Data[i, 1])
				{
					ptPairs.Add(new KeyValuePair<int, int>(i, indices.Data[i, 0]));
					pairCount++;
				}
			}

			//return the pair count
			return pairCount;
		}
		public static void CalculateResults(ref ImageMatch imageMatch)
		{
			List<KeyValuePair<int, int>> ptPairs = new List<KeyValuePair<int, int>>();

			int pairCount = FlannFindPairs(imageMatch.GivenImageFeatures, imageMatch.TemplateImageFeatures, ref ptPairs);

			imageMatch.MatchedPairs = ptPairs;
			imageMatch.TotalPairMatches = pairCount;
			imageMatch.TotalPossiblePairMatches = (imageMatch.GivenImageFeatures.Length / 2) + ( imageMatch.TemplateImageFeatures.Length / 2);

			imageMatch.PctOfMatch = ((float)imageMatch.TotalPairMatches/(float)imageMatch.TotalPossiblePairMatches) * 100;
		}

		public static bool GetFilesFromFolder(String rootFolder, String extensionFilter, bool recursive, ref List<String> fileList)
		{			
			//Get the list of directories
			IEnumerable<String> dirs = Directory.EnumerateDirectories(rootFolder);
			IEnumerable<String> files = Directory.EnumerateFiles(rootFolder);

			if (recursive)
			{
				foreach (var dir in dirs)
				{
					GetFilesFromFolder(dir, extensionFilter, recursive, ref fileList);
				}
			}

			foreach (var file in files)
			{
				if (file.ToLower().EndsWith(extensionFilter))
					fileList.Add(file);
			}

			return true;
		}

		public static ImageMatch FindSingleBestImageFile(String imageFileName, String mintStore, bool recursive, bool displayResult)
		{

			List<String> imageList = new List<string>();
			GetFilesFromFolder(mintStore, ".jpg", true, ref imageList);


			ImageMatch bestImageMatch = new ImageMatch();
			
			int bestMatch = 0;

			foreach (var image in imageList)
			{
				ImageMatch contextImageMatch = new ImageMatch();
				contextImageMatch.LoadGivenImageFromImageFile(imageFileName);

				contextImageMatch.LoadTemplateImageFromImageFile(image);

				CalculateResults(ref contextImageMatch);

				if (contextImageMatch.TotalPairMatches > bestMatch)
				{
					bestImageMatch = contextImageMatch;
					bestMatch = bestImageMatch.TotalPairMatches;
				}

			}

			//Merge the object image and the observed image into one image for display
			if (displayResult)
			{
				Image<Gray, Byte> res = bestImageMatch.GivenImage.ConcateVertical(bestImageMatch.TemplateImage);
				ImageViewer.Show(res, "Best image");
			}

			return bestImageMatch;
		}

		public static void ExtractStampText(ImageMatch imageMatch)
		{
			Tesseract _ocr = new Tesseract();

			

			_ocr.Init("E:\\Projects\\Scratch\\StampsForAfricaMVC\\Debug\\tessdata\\", "eng", false);
			//List<Word> words;

			//using (Bitmap bmp = imageMatch.GivenImage.Bitmap)
			//  words = _ocr.DoOCR(bmp, imageMatch.GivenImage.ROI);

			//_ocr.Dispose();
		}
		/*
		public static Stamp FindSingleBestImageFileDb(String imageFileName, ref ImageMatch bestImageMatch, bool displayResult)
		{

			List<Stamp> stamps = DbAccess.RetrieveStamps();
			
			Stamp bestDbStamp = null;

			int bestMatch = 0;

			ImageMatch contextImageMatch = new ImageMatch();
			contextImageMatch.LoadGivenImageFromImageFile(imageFileName);


			foreach (var stamp in stamps)
			{

				StampFeature feature = DbAccess.GetStampFeature(stamp.FeatureId);
				
				MemoryStream memoryStream = new MemoryStream(feature.Features.ToArray());
				var binFormatter = new BinaryFormatter();
				ImageFeature[] imageFeatures = (ImageFeature[]) binFormatter.Deserialize(memoryStream);
				contextImageMatch.TemplateImageFeatures = imageFeatures;
				memoryStream.Close();
			
				CalculateResults(ref contextImageMatch);

				if (contextImageMatch.TotalPairMatches > bestMatch)
				{
					bestImageMatch = contextImageMatch;
					bestMatch = bestImageMatch.TotalPairMatches;
					bestDbStamp = stamp;
				}

			}

			//Merge the object image and the observed image into one image for display
			if (displayResult)
			{

				Byte[] imageArray = bestDbStamp.Image.ToArray();

				File.WriteAllBytes("tempImage.jpg", imageArray);
				bestImageMatch.LoadTemplateImageFromImageFile("tempImage.jpg", false);
			
				Image<Gray, Byte> res = bestImageMatch.GivenImage.ConcateVertical(bestImageMatch.TemplateImage);
				ImageViewer.Show(res, "Best image");

				File.Delete("tempImage.jpg");
			}

			return bestDbStamp;

		}
		
		public static MatchResults FindBestImagesMatches(String imageFileName, int maxResults)
		{
			//Get the list of stamps inside the database
			List<Stamp> stamps = DbAccess.RetrieveStamps();
			MatchResults matchResults = new MatchResults();
			matchResults.MaxResults = maxResults;
			Stamp bestDbStamp = null;

			int bestMatch = 0;

			ImageMatch contextImageMatch = new ImageMatch();
			contextImageMatch.LoadGivenImageFromImageFile(imageFileName);
			
			foreach (var stamp in stamps)
			{

				StampFeature feature = DbAccess.GetStampFeature(stamp.FeatureId);

				MemoryStream memoryStream = new MemoryStream(feature.Features.ToArray());
				var binFormatter = new BinaryFormatter();
				ImageFeature[] imageFeatures = (ImageFeature[])binFormatter.Deserialize(memoryStream);
				contextImageMatch.TemplateImageFeatures = imageFeatures;
				memoryStream.Close();
				contextImageMatch.MatchedDBStamp = stamp;
				CalculateResults(ref contextImageMatch);

				
				matchResults.InsertImageMatch(contextImageMatch);
			}

			return matchResults;

		}
		*/
	}
}
